;主引导程序 
;------------------------------------------------------------
%include "boot.inc"


BaseOfLoader	equ	0x00		;loader的基址
OffsetOfLoader	equ	0x900		;loader的偏移地址

RootDirSectors	equ	14			;根目录所占多少扇区
SectorNumOfRootDirStart	equ	19	;根目录起始地址
SectorNumOfFAT1Start	equ	1	;簇目录开始扇区
SectorBalance	equ	17			;用于找到实际簇表项的平衡数

SECTION MBR vstart=0x7c00  

	jmp	short Label_Start

	nop
	BS_OEMName	db	'MINEboot'
	BPB_BytesPerSec	dw	512
	BPB_SecPerClus	db	1
	BPB_RsvdSecCnt	dw	1
	BPB_NumFATs	db	2
	BPB_RootEntCnt	dw	224
	BPB_TotSec16	dw	2880
	BPB_Media	db	0xf0
	BPB_FATSz16	dw	9
	BPB_SecPerTrk	dw	18
	BPB_NumHeads	dw	2
	BPB_HiddSec	dd	0
	BPB_TotSec32	dd	0
	BS_DrvNum	db	0
	BS_Reserved1	db	0
	BS_BootSig	db	0x29
	BS_VolID	dd	0
	BS_VolLab	db	'boot loader'
	BS_FileSysType	db	'FAT12   '

Label_Start:  

	mov ax,cs      
	mov ds,ax
	mov es,ax
	mov ss,ax
	mov fs,ax
	mov sp,0x7c00
	mov ax,0xb800
	mov gs,ax

; 清屏
;利用0x06号功能，上卷全部行，则可清屏。
; -----------------------------------------------------------
;INT 0x10   功能号:0x06	   功能描述:上卷窗口
;------------------------------------------------------------
;输入：
;AH 功能号= 0x06
;AL = 上卷的行数(如果为0,表示全部)
;BH = 上卷行属性
;(CL,CH) = 窗口左上角的(X,Y)位置
;(DL,DH) = 窗口右下角的(X,Y)位置
;无返回值：
;----------------------------------------------------------
   mov     ax, 0600h
   mov     bx, 0700h			
   mov     cx, 0				; 左上角: (0, 0)
   mov     dx, 184fh			; 右下角: (80,25),
								; 因为VGA文本模式中，一行只能容纳80个字符,共25行。
								; 下标从0开始，所以0x18=24,0x4f=79
   int     10h					; int 10h

   ; 输出字符串:MBR
   mov byte [gs:0x00],'M'
   mov byte [gs:0x01],0xA4	;A表示绿色背景闪烁，4表示前景色为红色

   mov byte [gs:0x02],'B'
   mov byte [gs:0x03],0xA4

   mov byte [gs:0x04],'R'
   mov byte [gs:0x05],0xA4	   
       
;=======	reset floppy

	xor	ah,	ah
	xor	dl,	dl
	int	13h

;=======	search loader.bin
	mov	word	[SectorNo],	SectorNumOfRootDirStart ;根目录从第19扇区开始

Lable_Search_In_Root_Dir_Begin:

	cmp	word [RootDirSizeForLoop], 0	;判断根目录是否遍历完
	jz	Label_No_LoaderBin				;遍历完仍然没有找到，跳转错误提示
	
	dec	word [RootDirSizeForLoop]		;遍历次数减一
	mov	ax,	00h							
	mov	es,	ax							;给es置0
	mov	bx,	8000h						;缓冲区偏移地址
	mov	ax,	[SectorNo]					;当前扇区数
	mov	cl,	1							;cl暂存读取多少个扇区数
	call Func_ReadOneSector				;跳转到磁盘读取函数
			
	mov	si,	LoaderFileName			
	mov	di,	8000h						;读取磁盘后的缓冲区开头地址
	cld									;flag的方向标志位df清零，
										;使变址寄存器si或di的地址指针自动增加，对应相反的是std
			
	mov	dx,	10h							;根目录表每项32B,512B/32B=16=10h

Label_Search_For_LoaderBin:

	cmp	dx,	0
	jz	Label_Goto_Next_Sector_In_Root_Dir;当扇区内16项，比对完后去读取下一个扇区
	dec	dx						;每次操作减一，计数
	mov	cx,	11					;文件名字为11B

Label_Cmp_FileName:

	cmp	cx,	0
	jz	Label_FileName_Found	;当11位都比对无误，文件名找到，跳转
	dec	cx						;每次操作减一，计数
	lodsb						;把si指向的存储单元读入al,
								;如果LODSW是读入AX,前面cld设置了自增方向，所以si自加1
	cmp	al,	byte [es:di]
	jz	Label_Go_On				;比较是字母一样则继续
	jmp	Label_Different			;比较不一样时

Label_Go_On:
	
	inc	di						;di+1,对应前面的lodsb的si自增1
	jmp	Label_Cmp_FileName		;继续比对

Label_Different:

	and	di,	0ffe0h				;因为每项目录32B，以32B格式化
	add	di,	20h					;到下一个32B
	mov	si,	LoaderFileName		;重新到达文件名最开头
	jmp	Label_Search_For_LoaderBin		;对下一项目录继续匹配

Label_Goto_Next_Sector_In_Root_Dir:
	
	add	word	[SectorNo],	1			;当前扇区加1
	jmp	Lable_Search_In_Root_Dir_Begin	;读取下一个根目录扇区
	

;=======	display on screen : ERROR:No LOADER Found

Label_No_LoaderBin:

	mov	ax,	1301h				;======================
	mov	bx,	008ch				;
	mov	dx,	0100h				;
	mov	cx,	21					;
	push	ax					;
	mov	ax,	ds					;闪烁显示未找到文件提示
	mov	es,	ax					;
	pop	ax						;
	mov	bp,	NoLoaderMessage		;
	int	10h						;
	jmp	$						;======================


;=======	found loader.bin name in root director struct

Label_FileName_Found:

	mov	ax,	RootDirSectors		;ax=14
	and	di,	0ffe0h				;32B格式化
	add	di,	01ah				;读取目标文件的起始簇号
	mov	cx,	word [es:di]		;把文件起始簇号给cx
	push	cx					;入栈
	add	cx,	ax					;cx=起始簇号+14
	add	cx,	SectorBalance		;cx=起始簇号+14+17
	mov	ax,	BaseOfLoader		;ax=0x00
	mov	es,	ax					;把0x00赋值给es
	mov	bx,	OffsetOfLoader		;把0x900赋值给bx 
								;把0x900设置成磁盘读取的缓冲区
	mov	ax,	cx					;把起始簇号+14+17给ax作为磁盘读取的起始地址

Label_Go_On_Loading_File:	
	push	ax					;====================
	push	bx					;
	mov	ah,	0eh					;
	mov	al,	'.'					;每读取一个扇区画一个"."
	mov	bl,	0fh					;
	int	10h						;
	pop	bx						;
	pop	ax						;====================

	mov	cl,	1					;读取的扇区数为1，暂存在cl里
	call Func_ReadOneSector		;读一个扇区函数

	pop	ax						;把文件起始簇号再出栈给ax，
								;对应上面push cx，和下面的push ax下一个簇相对簇号
	call Func_GetFATEntry	

	cmp	ax,	0fffh				;判断是否文件最后一个结束簇标识0fffh
	jz	Label_File_Loaded		;跳转到文件中

	push	ax					;把下一个相对簇号入栈，对应前面pop ax	
	mov	dx,	RootDirSectors		;dx=14
	add	ax,	dx					;ax+14
	add	ax,	SectorBalance		;ax+14+17
	add	bx,	[BPB_BytesPerSec]	;bx=512
	jmp	Label_Go_On_Loading_File;跳转去加载下一个簇号指示下的下一个扇区

Label_File_Loaded:
	jmp	BaseOfLoader:OffsetOfLoader + 0x300

;=======	read one sector from floppy

Func_ReadOneSector:

	;=========================================================
	;lba转chs公式:	 逻辑扇区号=(柱面*磁头数)*每个磁道扇区数+扇区号-1
	;				扇区号=逻辑扇区号%磁道内扇区数+1
	;				(柱面*磁头数)=磁道数=(逻辑扇区号/磁道内扇区数)
	;=========================================================

	push bp						; 把bp保存下来入栈
	mov	bp,	sp					; 把当前栈给bp
	sub	esp, 2					; 栈顶指向
	mov	byte [bp - 2], cl		; 把cl放入栈顶,cl=1,[bp-2]=1
	push bx						; 把bx缓冲区偏移地址保存入栈 bx=8000h
	mov	bl,	[BPB_SecPerTrk]		; 每个扇区的磁道数量放入bl
	div	bl						; ax除以bl,保存在ax
	inc	ah						; ah暂存扇区号，起始扇区号为1，所以加1
	mov	cl,	ah					; 把扇区号给cl，int 13h
	mov	dh,	al					; 把(柱面*磁头数)暂存到dh
	shr	al,	1					; al右移一位，al除以两个磁头数
	mov	ch,	al					; 把柱面号给ch，int 13h
	and	dh,	1					; 判断磁头数为几，int 13h
	pop	bx						; 设置缓冲区为es:8000h，int 13h
	mov	dl,	[BS_DrvNum]			; 把软盘号给dl
Label_Go_On_Reading:
	mov	ah,	2					; ah=2模式 读取磁盘 int 13
	mov	al,	byte [bp - 2]		; [bp-2]=1,al=1,读取一个扇区 int 13h
	int	13h
	jc	Label_Go_On_Reading		; cf=0时读取完毕
	add	esp, 2					; 把栈回到指向bp
	pop	bp						; 恢复bp
	ret

;=======	get FAT Entry

Func_GetFATEntry:

	push	es
	push	bx
	push	ax
	mov	ax,	00
	mov	es,	ax
	pop	ax						;ax=起始簇号(FAT表项号)
	mov	byte [Odd],	0			;初始化odd
	mov	bx,	3					;===========
	mul	bx						;ax乘以3除以2，(每个表象12bit=1.5B)
	mov	bx,	2					;ax=1.5B*ax
	div	bx						;===========
	cmp	dx,	0					;判断是否有余数，有余数说明是从后半个字节开始的
	jz	Label_Even				;
	mov	byte [Odd],	1			;有余数置1

Label_Even:

	xor	dx,	dx					;清零dx
	mov	bx,	[BPB_BytesPerSec]	;bx=512
	div	bx						;ax=(1.5B*ax)/512。表示表项扇区的第几扇区
	push	dx					;把表项在扇区内的位置入栈
	mov	bx,	8000h				;es:bx的int 13读盘缓冲区设置
	add	ax,	SectorNumOfFAT1Start;ax加上表项扇区起始扇区
	mov	cl,	2					;cl暂存读取的扇区个数
	call	Func_ReadOneSector	;读取扇区
	
	pop	dx						;dx=表项在扇区内的位置
	add	bx,	dx					;bx是缓冲区的开始，加上表项在扇区的位置
	mov	ax,	[es:bx]				;把表项开始的双字节（16bit，2B）给ax
	cmp	byte [Odd],	1			;比较是奇数项还是偶数项
	jnz	Label_Even_2			;当是单数项跳转
	shr	ax,	4					;是偶数项取前12位

Label_Even_2:
	and	ax,	0fffh				;是奇数项时取后12位
	pop	bx
	pop	es
	ret

;=======	tmp variable

RootDirSizeForLoop	dw	RootDirSectors	;初始值14
SectorNo		dw	0
Odd			db	0

;=======	display messages

NoLoaderMessage:	db	"ERROR:No LOADER Found"
LoaderFileName:		db	"LOADER  BIN",0

   times 510-($-$$) db 0
   db 0x55,0xaa
